// FeedService.cs
// Facebook/Framework/Service
// Copyright (c) 2007, Nikhil Kothari. All Rights Reserved.
//

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using Facebook.Service.Core;

namespace Facebook.Service {

    /// <summary>
    /// Represents the service APIs related to the user's feeds on Facebook.
    /// </summary>
    public sealed class FeedService {

        private FacebookSession _session;

        internal FeedService(FacebookSession session) {
            Debug.Assert(session != null);
            _session = session;
        }

        /// <summary>
        /// Publishes a story to the user's news feed. This corresponds to the feed.publishStoryToUser
        /// Facebook method.
        /// </summary>
        /// <param name="story">The story to publish.</param>
        /// <returns>true on success; false otherwise.</returns>
        public bool PublishNewsFeedStory(FeedStory story) {
            if (story == null) {
                throw new ArgumentNullException("story");
            }
            if (story.IsTemplated) {
                throw new InvalidOperationException("A news feed story cannot use templates.");
            }

            return PublishStory("feed.publishStoryToUser", story);
        }

        /// <summary>
        /// Publishes a story to the user's mini-feed, and the user's friends' news feed.
        /// This corresponds to the feed.publishActionOfUser or feed.publishTemplatizedAction
        /// Facebook methods.
        /// </summary>
        /// <param name="story">The story to publish.</param>
        /// <returns>true on success; false otherwise.</returns>
        public bool PublishMiniFeedStory(FeedStory story) {
            if (story == null) {
                throw new ArgumentNullException("story");
            }

            if (story.IsTemplated) {
                return PublishTemplatedStory(story);
            }
            else {
                return PublishStory("feed.publishActionOfUser", story);
            }
        }

        private bool PublishStory(string method, FeedStory story) {
            FacebookRequest request = new FacebookRequest(_session);
            request.Parameters["title"] = story.Title;
            if (String.IsNullOrEmpty(story.Body) == false) {
                request.Parameters["body"] = story.Body;
            }
            if (story.Images != null) {
                int imageNumber = 1;

                foreach (FeedImage image in story.Images) {
                    request.Parameters["image_" + imageNumber] = image.ImageUrl;
                    request.Parameters["image_" + imageNumber + "_link"] = image.ImageLinkUrl;

                    imageNumber++;
                }
            }

            FacebookResponse response = request.InvokeMethod(method);
            if (response.Status == FacebookResponseStatus.Succeeded) {
                return FacebookResponse.CoerceBoolean(response.Result);
            }

            return false;
        }

        private bool PublishTemplatedStory(FeedStory story) {
            FacebookRequest request = new FacebookRequest(_session);
            request.Parameters["title_template"] = story.Template.TitleTemplate;
            if (story.TitleTemplateValues != null) {
                StringBuilder titleDataBuilder = new StringBuilder();

                JsonWriter writer = new JsonWriter(new StringWriter(titleDataBuilder), /* minimizeWhitespace */ true);
                writer.WriteValue(story.TitleTemplateValues);

                request.Parameters["title_data"] = titleDataBuilder.ToString();
            }

            request.Parameters["actor_id"] = story.UserID;

            if (story.TargetUserIDs != null) {
                StringBuilder targetIDsBuilder = new StringBuilder();
                foreach (string id in story.TargetUserIDs) {
                    if (targetIDsBuilder.Length != 0) {
                        targetIDsBuilder.Append(",");
                    }
                    targetIDsBuilder.Append(id);
                }

                request.Parameters["target_ids"] = targetIDsBuilder.ToString();
            }

            if (String.IsNullOrEmpty(story.Template.BodyTemplate) == false) {
                request.Parameters["body_template"] = story.Template.BodyTemplate;
            }
            if (String.IsNullOrEmpty(story.Body) == false) {
                request.Parameters["body_general"] = story.Body;
            }
            if (story.BodyTemplateValues != null) {
                StringBuilder bodyDataBuilder = new StringBuilder();

                JsonWriter writer = new JsonWriter(new StringWriter(bodyDataBuilder), /* minimizeWhitespace */ true);
                writer.WriteValue(story.BodyTemplateValues);

                request.Parameters["body_data"] = bodyDataBuilder.ToString();
            }

            if (story.Images != null) {
                int imageNumber = 1;

                foreach (FeedImage image in story.Images) {
                    request.Parameters["image_" + imageNumber] = image.ImageUrl;
                    request.Parameters["image_" + imageNumber + "_link"] = image.ImageLinkUrl;

                    imageNumber++;
                }
            }

            FacebookResponse response = request.InvokeMethod("feed.publishTemplatizedAction");
            if (response.Status == FacebookResponseStatus.Succeeded) {
                return FacebookResponse.CoerceBoolean(response.Result);
            }

            return false;
        }

        /// <summary>
        /// Publishes a story to the user's news feed. This corresponds to the feed.publishStoryToUser
        /// Facebook method.
        /// </summary>
        /// <param name="title">The title of the story.</param>
        /// <param name="body">An optional body containing the story.</param>
        /// <param name="images">An optional set of up to 4 images associated with the story.</param>
        /// <returns>true on success; false otherwise.</returns>
        [Obsolete("Use PublishNewsFeedStory(FeedStory)")]
        public bool PublishNewsFeedStory(string title, string body, ICollection<FeedImage> images) {
            return PublishStory("feed.publishStoryToUser", title, body, images);
        }

        /// <summary>
        /// Publishes a story to the user's mini-feed, and the user's friends' news feed.
        /// This corresponds to the feed.publishActionOfUser Facebook method.
        /// </summary>
        /// <param name="title">The title of the story.</param>
        /// <param name="body">An optional body containing the story.</param>
        /// <param name="images">An optional set of up to 4 images associated with the story.</param>
        /// <returns>true on success; false otherwise.</returns>
        [Obsolete("Use PublishMiniFeedStory(FeedStory)")]
        public bool PublishMiniFeedStory(string title, string body, ICollection<FeedImage> images) {
            return PublishStory("feed.publishActionOfUser", title, body, images);
        }

        private bool PublishStory(string method, string title, string body, ICollection<FeedImage> images) {
            if (String.IsNullOrEmpty(title)) {
                throw new ArgumentNullException("title");
            }
            if ((images != null) && (images.Count > 4)) {
                throw new ArgumentException("A maximum of 4 images are allowed.");
            }

            FacebookRequest request = new FacebookRequest(_session);
            request.Parameters["title"] = title;
            if (String.IsNullOrEmpty(body) == false) {
                request.Parameters["body"] = body;
            }
            if (images != null) {
                int imageNumber = 1;

                foreach (FeedImage image in images) {
                    request.Parameters["image_" + imageNumber] = image.ImageUrl;
                    request.Parameters["image_" + imageNumber + "_link"] = image.ImageLinkUrl;

                    imageNumber++;
                }
            }

            FacebookResponse response = request.InvokeMethod(method);
            if (response.Status == FacebookResponseStatus.Succeeded) {
                return FacebookResponse.CoerceBoolean(response.Result);
            }

            return false;
        }
    }
}
